/*
 *  Copyright 2011 Alexey Andreev.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.xthl.core;

import java.util.ArrayList;
import java.util.List;
import org.nop.core.AbstractController;
import org.nop.core.Content;
import org.nop.core.RouteBinding;
import org.nop.forms.Paginator;
import org.nop.rmi.RmiSession;
import org.nop.templating.Template;
import org.nop.util.Injected;
import org.xthl.core.api.*;
import org.xthl.core.lang.LabelsBundle;
import org.xthl.core.lang.ValidationBundle;
import org.xthl.core.view.*;

/**
 *
 * @author Alexey Andreev
 */
@RouteBinding(UsersRoute.class)
public class UsersController extends AbstractController {
    private static final int usersLimit = 1000;
    private UserLogic logic;
    private RoleLogic roleLogic;
    private UsersConfigService config;
    private UserAuthenticator authenticator;
    private FragmentRepository fragmentRepository;
    
    @Injected
    public UsersController(UserLogic logic, RoleLogic roleLogic, UsersConfigService config,
            UserAuthenticator authenticator) {
        this.logic = logic;
        this.roleLogic = roleLogic;
        this.config = config;
        this.authenticator = authenticator;
    }
    
    @Injected
    public void setFragmentRepository(FragmentRepository fragmentRepository) {
        this.fragmentRepository = fragmentRepository;
    }

    public Content list(SearchUserForm form) {
        Paginator paginator = form.getPaginator();
        paginator.setItemsPerPage(20);
        String searchString = form.getSearchString();
        int sz = Math.min(usersLimit, paginator.getCurrent() + 100);
        List<User> users = searchString != null && !searchString.isEmpty() ?
                logic.findUsers(searchString, sz) : logic.getLastUsers(sz);
        paginator.setItemCount(users.size());
        return html(createView(UserListView.class)
                .setUsers(users)
                .setForm(form)
                .setCanEnable(logic.checkPermission("user.enable"))
                .setCanActivate(logic.checkPermission("user.accept")));
    }
    
    public Content disable(SearchUserForm form) {
        try {
            logic.enableUsers(form.getSelectedUserIds(), false);
            form.getValidation().add(bundle(LabelsBundle.class).usersDisabled());
            form.getSelectedUserIds().clear();
        } catch (RestrictedAccessException ex) {
            form.getValidation().add(bundle(LabelsBundle.class).couldNotDisableUsers());
        }
        return list(form);
    }
    
    public Content enable(SearchUserForm form) {
        try {
            logic.enableUsers(form.getSelectedUserIds(), true);
            form.getValidation().add(bundle(LabelsBundle.class).usersEnabled());
            form.getSelectedUserIds().clear();
        } catch (RestrictedAccessException ex) {
            form.getValidation().add(bundle(LabelsBundle.class).couldNotEnableUsers());
        }
        return list(form);
    }
    
    public Content activate(SearchUserForm form) {
        logic.activateUsers(form.getSelectedUserIds());
        form.getValidation().add(bundle(LabelsBundle.class).usersActivated());
        form.getSelectedUserIds().clear();
        return list(form);
    }
    
    private boolean isSelf(int userId) {
        UserDetails currentUser = logic.getCurrentUser();
        return currentUser != null && currentUser.getId() == userId;
    }
    
    public Content view(int userId) {
        UserDetails user = logic.getUser(userId);
        if (user == null) {
            return null;
        }
        boolean editable = false;
        boolean changePasswordAvailable = false;
        if (isSelf(userId)) {
            changePasswordAvailable = true;
        }
        if (user.isSpecial()) {
            editable = logic.checkPermission("user.manage");
            changePasswordAvailable |= editable;
        } else {
            editable = logic.checkPermission("user.edit");
            changePasswordAvailable |= logic.checkPermission("user.edit");
        }
        boolean permissionsAvailable = logic.checkPermission("user.edit");
        boolean emailAvailable = isSelf(userId) || logic.checkPermission("user.edit");
        List<Template> extensions = new ArrayList<Template>();
        for (UserProfileFragment fragment : fragmentRepository.getFragments(
                UserProfileFragment.class)) {
            fragment.setUser(user);
            extensions.add(fragment.render());
        }
        UserView view = createView(UserView.class)
                .setUser(user)
                .setEditable(editable)
                .setChangePasswordAvailable(changePasswordAvailable)
                .setEmailAvailable(emailAvailable)
                .setPermissionsAvailable(permissionsAvailable)
                .setExtensions(extensions);
        if (permissionsAvailable) {
            view.setPermissions(logic.getUserPermissions(userId));
        }
        if (emailAvailable) {
            view.setEmail(logic.getUserEmail(userId));
        }
        return html(view);
    }
    
    public Content accountCreation(CreateAccountForm form) {
        logic.requirePermission("user.create");
        return html(createView(CreateAccountView.class).setForm(form));
    }
    
    public Content createAccount(CreateAccountForm form) {
        if (logic.getUserByLogin(form.getLogin()) != null) {
            ValidationBundle bundle = bundle(ValidationBundle.class);
            form.getValidation().add(bundle.loginOccupied());
            return accountCreation(form);
        }
        CreateUserCommand cmd = new CreateUserCommand();
        cmd.setLogin(form.getLogin());
        cmd.setPassword(form.getPassword());
        cmd.setEmail(form.getEmail());
        UserDetails user = logic.createUser(cmd);
        return html(createView(AccountCreatedView.class).setUser(user)
                .setActivationMode(config.getActivationMode()));
    }
    
    public Content login(LoginForm form) {
        form.setLastUrl(currentUrl);
        return html(createView(LoginView.class)
                .setForm(form)
                .setRegistrationAvailable(logic.checkPermission("user.create")));
    }
    
    public Content enter(LoginForm form) {
        try {
            RmiSession session = authenticator.login(form.getLogin(), form.getPassword());
            UserLogic userLogic = (UserLogic)session.get(UserLogic.class);
            final int id = userLogic.getCurrentUser().getId();
            services.putSessionBean(UserSession.class, new UserSession() {
                @Override
                public int getUserId() {
                    return id;
                }
            });
            return redirect(form.getLastUrl());
        } catch (AuthenticationException e) {
            form.getValidation().add(bundle(ValidationBundle.class).authenticationError());
            return login(form);
        } catch (AccountDisabledException e) {
            form.getValidation().add(bundle(ValidationBundle.class).accountDisabled());
            return login(form);
        }
    }
    
    public Content logout(@SuppressWarnings("unused") LogoutForm form) {
        return null;
    }
    
    public Content exit(LogoutForm form) {
        services.putSessionBean(UserSession.class, null);
        return redirect(form.getBackUrl());
    }
    
    public Content edit(UserEditForm form, int userId) {
        logic.checkPermission("user.edit");
        UserDetails user = logic.getUser(userId);
        if (user == null) {
            return null;
        }
        if (!form.isPosted()) {
            form.getPermissions().addAll(logic.getUserPermissions(userId));
            for (Role role : user.getRoles()) {
                form.getRoleIds().add(role.getId());
            }
        }
        return html(createView(UserEditView.class)
                .setAllRoles(roleLogic.getRoles(RoleTypeFilter.ALL))
                .setAllPermissions(roleLogic.getPermissions())
                .setForm(form)
                .setLogin(user.getLogin())
                .setUserId(userId));
    }
    
    public Content save(UserEditForm form, int userId) {
        logic.setUserRoles(userId, form.getRoleIds());
        logic.setUserPermissions(userId, form.getPermissions());
        return redirect(route(UsersRoute.class).view(userId));
    }
    
    public Content changePassword(ChangePasswordForm form, int userId) {
        UserDetails user = logic.getUser(userId);
        if (user == null) {
            return null;
        }
        return html(createView(ChangePasswordView.class)
                .setForm(form)
                .setLogin(user.getLogin())
                .setUserId(userId));
    }
    
    public Content setNewPassword(ChangePasswordForm form, int userId) {
        if (!logic.checkPassword(userId, form.getCurrentPassword())) {
            form.getValidation().add(bundle(ValidationBundle.class).wrongCurrentPassword());
            return changePassword(form, userId);
        }
        logic.setUserPassword(userId, form.getNewPassword());
        return redirect(route(UsersRoute.class).view(userId));
    }
}
